#pragma  once

#include"Log.hpp"
#include<vector>
#include<fcntl.h>
#include<sstream>
#include<unistd.h>
#include"Util.hpp"
#include<stdlib.h>
#include<algorithm>
#include<sys/stat.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unordered_map>
#include<sys/sendfile.h>

#define VERSION "HTTP/1.0"

#define HEADER_SEP ": "
#define URL_SEP "?"
#define LINE "\r\n"

#define WEB_ROOT "WebRoot"
#define PAGE_DIR "page"
#define HOME_PAGE "index.html"
#define PAGE_400 "400.html"
#define PAGE_404 "404.html"

#define CGI_DIR "cgi"

#define OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404

class HttpRequest
{
 public:
   std::string request_line;
   std::vector<std::string> request_header;
   std::string request_blankline;
   int content_length;
   std::string request_body;

 public:
   std::string request_method;
   std::string request_url;
   std::string request_version;
   std::unordered_map<std::string,std::string> request_header_kv;
 
 public:
   std::string request_path;
   std::string request_param;
   

public:
   HttpRequest():request_blankline("\n"),content_length(-1)
   {}
   
   
};

class HttpResponse
{
  public:
    std::string response_line;
    std::vector<std::string> response_header;
    std::string response_blankline;
    std::string response_body;
  public:
    int  response_status_code;
    std::string response_descOfcode;
    std::string response_version;
  public:
    int response_body_fd;
    int response_body_fsize;

  public:
    HttpResponse():response_blankline(LINE),response_body_fd(-1),response_body_fsize(0)
    {}

  static std::string code2desc(int status_code)
  {
    std::string desc;
    switch(status_code)
    {
      case 200:
       desc="OK";
       break;
      case 400:
       desc="Bad Request";
      case 404:
       desc="Not Found";
       break;
      default:
       break;
    }
    return desc;
  }
};

class EndPoint
{
  private:
   int _sock;
   HttpRequest  _http_request; 
   HttpResponse _http_response;
   bool _cgi;
  public:
   EndPoint(int sock):_sock(sock),_cgi(false)
   {}
   ~EndPoint()
   { 
     if(_sock>0) close(_sock);
   }

  private:
   bool RecvHttpRequestLine()
   {
     auto& request_line=_http_request.request_line;
     int ret=Util::ReadLine(_sock,request_line);
     LOG(INFO,request_line.substr(0,request_line.size()-1));
     
     if(ret<0)
     {
      LOG(WARNING,"RecvHttpRequestLine Error");
      return false;
     }
     LOG(INFO,"RecvHttpRequestLine Success!");
     return true;
   }
   bool RecvHttpRequestHeader()
   {
     int ret;

     auto& header=_http_request.request_header;
     std::string str;
     while((ret = Util::ReadLine(_sock,str)) > 0 && str!="\n")
     {
      header.push_back(str);  
      LOG(INFO,str.substr(0,str.size()-1));
      str.clear();
     }

     if(str=="\n")  _http_request.request_blankline="\n";
     LOG(INFO,str.substr(0,str.size()-1));
    
     if(ret < 0)
     {
       LOG(WARNING,"RecvHttpRequestHeader Error");
       return false;
     }
       
     LOG(INFO,"RecvHttpRequestHeader Success!");
     return true;

   }

   bool ParseHttpRequestLine()
   {
      auto& method = _http_request.request_method;
      auto& url = _http_request.request_url;
      auto& version = _http_request.request_version;
      
      std::stringstream  request_line(_http_request.request_line); 
      request_line >> method;
      request_line >> url;
      request_line >> version;

      if(method=="" || url=="" || version=="")
      {
        LOG(WARNING,"HttpRequestLine Is Not Complete!");
        return false;       
      }

      std::transform(method.begin(),method.end(),method.begin(),::toupper);
      std::transform(version.begin(),version.end(),version.begin(),::toupper);

      LOG(INFO,method);
      LOG(INFO,url);
      LOG(INFO,version);

      auto& path = _http_request.request_path;
      auto& param = _http_request.request_param;

      if(method == "GET")
      {
        Util::CutString(url,path,param,URL_SEP);
        LOG(INFO,path);
        LOG(INFO,param);
      }

      return true;
   }

   bool ParseHttpRequestHeader()
   {
     for(auto& iter:_http_request.request_header)
     {
         std::string key;
         std::string value;
        if( Util::CutString(iter,key,value,HEADER_SEP) ==  0 )
        {
        value.resize(value.size()-1); 
         _http_request.request_header_kv.insert({key,value});   
        }
        else 
        {
         LOG(WARNING,"RecvHttpRequestHeader Is Not Right"); 
         return false;
        }
     }

     return true;
   }

   bool RecvHttpRequestBody()
   {
     LOG(INFO,"-------RecvHttpRequestBody");
     int content_length=0;
     auto& header_kv =_http_request.request_header_kv;
     if(_http_request.request_method == "POST")
     {
       auto ret = header_kv.find("Content-Length");
        if(ret!=header_kv.end())
        {//方法为POST 且 有Content-Length长度
         content_length = atoi(ret->second.c_str());
        }
        else 
        {
          //方法为POST 但没有找到 Content-Length
          LOG(WARNING,"Method为POST但请求报头内未发现Content-Length");
          return false;
        }
     }
     else 
     { //方法为GET,不需读正文部分
       //返回
       return true;
     }

     //开始根据content_length读
     char buffer[1024]={0};
     int total=0;//已读到的总个数
     int expect_count= content_length>sizeof(buffer) ? sizeof(buffer):content_length;//读一次期待的个数
     int actual_count=0;//实际读到的个数
     while( expect_count>0 && (actual_count=recv(_sock,buffer,expect_count,0)) > 0  )
     {
       std::cout<<"--"<<std::endl;
          buffer[actual_count]=0;
          _http_request.request_body+=buffer;
          buffer[0]=0;
          total+=actual_count;
          expect_count= content_length-total>sizeof(buffer) ? sizeof(buffer):content_length-total;//读一次期待的个数
     }

     LOG(INFO,std::string("Body")+=_http_request.request_body);
     //正文读完
     return true; 
   }

   bool BuildHttpResponseLine()
   {
     auto& response = _http_response;
     
     response.response_version=VERSION;
     response.response_descOfcode=HttpResponse::code2desc(response.response_status_code);

     if(response.response_descOfcode == "")
     {
       LOG(ERROR,"Can't find descOfcode!");
       return false;
     }

     response.response_line+=response.response_version;
     response.response_line+=" ";
     response.response_line+=std::to_string(response.response_status_code);
     response.response_line+=" ";
     response.response_line+=response.response_descOfcode;
     response.response_line+=LINE;

     return true;
   }


   bool BuildHttpResponseHeader()
   {
     std::string content_length("Content-Length: ");

     if(_cgi == false)
     {
       content_length+=std::to_string(_http_response.response_body_fsize);
     }
     else 
     {//_cgi == true
      content_length+=std::to_string(_http_response.response_body.size());
     }

     _http_response.response_header.push_back(content_length);
     
     return true;
   }
  
   bool CheckLegal()
   {//检查请求是否合法
     auto& response = _http_response;
     auto& method = _http_request.request_method;

      if(method!="GET" && method!="POST")   
      {//请求方法错误(只考虑GET和POST方法)

          response.response_status_code=BAD_REQUEST;//设置状态码
          std::string path;
          path+=WEB_ROOT;
          path+="/";
          path+=PAGE_DIR;
          path+="/";
          path+=PAGE_400;

          if((response.response_body_fd=open(path.c_str(),O_RDONLY)) > 0)
          {//以读方式成功打开文件
            //获取文件大小
            struct stat st;
            stat(path.c_str(),&st); 
            response.response_body_fsize=st.st_size;
            LOG(WARNING,"Request UnLegal!");
          }
          else 
          {
            //打开文件失败
          }
          return false;
      }
      
      LOG(INFO,"Request Legal");
      return true;

   }

   bool ProcessNonCgi()
   {
     std::string path;
     path+=WEB_ROOT;
     path+='/';
     path+=PAGE_DIR;
     path+=_http_request.request_path;
     LOG(INFO,path);

     struct stat st;
     if( stat(path.c_str(),&st) == 0 )
     {//文件存在
       LOG(INFO,path+"文件存在");
       if(S_ISDIR(st.st_mode))
       {//是文件夹
            if(path[path.size()-1] != '/')
            {
               path+='/';
            }
            path+=HOME_PAGE;
            if(stat(path.c_str(),&st) == 0)
            {//文件存在
                LOG(INFO,path+"文件存在");
                if(( _http_response.response_body_fd=open(path.c_str(),O_RDONLY)) > 0)
                {//打开成功
                 _http_response.response_body_fsize=st.st_size;//保存文件描述符，文件大小
                 LOG(INFO,"File open sucess"); 
                 _http_response.response_status_code=OK;
                 return true;//返回状态码
                }
                else 
                 {//打开失败
                  //服务器内部错误 ，return ...
                  return false;
                 }
             }
             else 
             {//文件不存在
              LOG(INFO,path);
              path="";
              path= std::string(WEB_ROOT) + "/" + PAGE_DIR + "/" + "404.html";
              _http_response.response_body_fd=open(path.c_str(),O_RDONLY);
              stat(path.c_str(),&st);
              _http_response.response_body_fsize=st.st_size;
              _http_response.response_status_code=NOT_FOUND;
              return true ; 
              }
       }
       else
       {//是文件
         if(( _http_response.response_body_fd=open(path.c_str(),O_RDONLY)) > 0)
         {//打开成功
            _http_response.response_body_fsize=st.st_size;//保存文件描述符，文件大小
               LOG(INFO,path+="File open sucess"); 
            _http_response.response_status_code=OK;
            return true;//返回状态码
         }
         else 
         {//打开失败
          //服务器内部错误 ，return ...
          return false;
         }
       }
       
       _http_response.response_status_code=OK;
       return true;
     }
     else 
     {//文件不存在
       
       LOG(INFO,path);
       path="";
       path= std::string(WEB_ROOT) + "/" + PAGE_DIR + "/" + "404.html";
       _http_response.response_body_fd=open(path.c_str(),O_RDONLY);
       stat(path.c_str(),&st);
       _http_response.response_body_fsize=st.st_size;
       _http_response.response_status_code=NOT_FOUND;
       return true;
     }
   }

   bool  ProcessCgi()
   {
     auto& method = _http_request.request_method;
     
     
     int readfd[2]={0};//[0]父进程读，[1]子进程写
     int writefd[2]={0};//[1]父进程写，[0]子进程读
     if(pipe(readfd)<0 || pipe(writefd)<0 )
     {
       //创建匿名管道失败
       return false;
       LOG(ERROR,"Pipe Error!!!");
     }
    
     pid_t pid = fork();
    
     if(pid == 0)
     {//子进程
       
       close(readfd[0]);
       close(writefd[1]);
       dup2(readfd[1],1);//将子进程std::cout 由 1 >> readfd[1]
        
       std::string env_method("Method=");
       env_method+=method;
       putenv((char*)env_method.c_str());
       std::string env_param("Param=");//这个变量创建为什么不放在下一个if里面呢
                                   //因为我发现放在里面导入变量虽然成功了
                                   //导入的不是Param，不知道为什么
       std::string env_content_length("Content_Length=");
       if(method == "GET")
       {//通过环境变量传参
       env_param+=_http_request.request_param;
       putenv((char*)env_param.c_str());
       }
       else if(method == "POST")
       {//通过管道传参c
         env_content_length+=std::to_string(_http_request.request_body.size());
         putenv((char*)env_content_length.c_str());
         std::cerr<<"Put"<<env_content_length<<std::endl;
         dup2(writefd[0],0);// 将子进程std::cin 由 0 >> writefd[0] 
       }
       std::string bin;
       bin = std::string(WEB_ROOT) + "/" + CGI_DIR + "/" + "test_cgi";
       execl(bin.c_str(),"test_cgi");
       
       //走到这里说明,进程替换失败
       return false; 
     }
     else if(pid > 0)
     {//父进程
       close(readfd[1]);
       close(writefd[0]);
        if(method == "POST")
        {
          const char* c_str = _http_request.request_body.c_str();
          int body_size=_http_request.request_body.size();
          int total=0;
          int size=0;
          while(( size = write(writefd[1],c_str+total,body_size-total)) > 0)
          {
               total+=size;
          }
        }

        //父进程从管道读取子进程消息
        char c; 
        while(read(readfd[0],&c,1) > 0)
            _http_response.response_body+=c;
 

        int status;
        //父进程等待子进程 
        if(waitpid(pid,&status,0) >= 0) 
        {//等待子进程成功
          LOG(INFO,"Wait Child Process Success");   
          if(WIFEXITED(status))
          {
            LOG(INFO,"Child Exit Normally");
            _http_response.response_status_code=OK;
            return true;
          }
          else 
          {
            LOG(INFO,"Child Exit Unnormally!!");
            return false;
          }

        }
        else 
        {//等待子进程失败
          LOG(ERROR,"Wait Child Process Error!");   
          return false;
        }
        
     }
     else 
     {//创建进程失败
      return false;
     }

     
     _http_response.response_status_code=OK;
     return true;  
   }


  public:
   bool RecvHttpRequest()
   {
     return  RecvHttpRequestLine() &&  RecvHttpRequestHeader();
   }
   bool  ParseHttpRequest()
   {
     return 
       ParseHttpRequestLine()
     &&ParseHttpRequestHeader()
     //读取并解析完请求报头后，查看Content-Length，决定读多少，防止粘包问题
     //这次模拟的http/1.0版本是无状态 不会粘包
     &&RecvHttpRequestBody();
   }
   bool HandleHttpRequest()
   {
     if(CheckLegal()==false) 
       return false;
       
     //请求合法
     
     auto& request = _http_request;
     if(request.request_method == "GET")
     {
       if(request.request_param.empty()==true)
       {//get方法,url无参
        _cgi=false;
       return  ProcessNonCgi();
       }
       else 
       {//get方法,url带参
        _cgi=true;
        return ProcessCgi();
       }
     }
     else if(request.request_method == "POST")
     {
       _cgi=true;
       return  ProcessCgi();
     }

     return false;
     
   }
   bool  BuildHttpResponse()
   {
   return  BuildHttpResponseLine() &&  BuildHttpResponseHeader();
   }
   bool SendHttpResonse()
   {
     auto& response = _http_response;

     send(_sock,response.response_line.c_str(),response.response_line.size(),0);
     
     for(auto& iter : response.response_header)
     {
       send(_sock,iter.c_str(),iter.size(),0);
       send(_sock,LINE,std::string(LINE).size(),0);
     }
     
     send(_sock,response.response_blankline.c_str(),response.response_blankline.size(),0);
     
     if(_cgi == false)
     {
       if(response.response_body_fd > 0 && response.response_body_fsize > 0)
       {
         sendfile(_sock,response.response_body_fd,nullptr,response.response_body_fsize); 
         close(response.response_body_fd);
         LOG(INFO,"Send Success!!!!!"+std::to_string(response.response_body_fsize));
         return true;
       }
       else 
       {
         // unexpected error
         LOG(WARNING,"未找到发送的文件!");
         return false;
       }
     }
     else 
     {//_cgi == true 
        send(_sock,response.response_body.c_str(),response.response_body.size(),0);
        return true;
     }

     return true;

   }
};
